package prueba;

//
// COMP102
// Example 14: Supermarket Checkout Simulation
//             Queues with random arrival and service times.
//
// Paul E. Dunne 24/11/99
//
import java.io.*;
public class Prueba
  {
  public static InputStreamReader input = new InputStreamReader(System.in);
  public static BufferedReader   keyboardInput = new BufferedReader(input);
  //************************************************************
  // User specified fields to be input at the simulation start *
  //************************************************************
  static int MAX_CUSTOMERS;             // Maximum number of customers in system.
  static int MAX_TILLS;                 // The number of checkout tills.
  static int MAX_ITEMS;                 // The maximum number of items.
  static int ROUNDS_TO_SIMULATE;        // Number of simulation rounds during which
                                        // new customers can arrive.
  static double ARRIVAL_RATE;           // Should be a value between 0.0 and 1.0,
                                        // higher value == more frequent arrivals.
  //***********************************************************
  // The following fields allow a user to specify statistical *
  // information that should be output.                       *
  //***********************************************************
  static int UPDATE_STATS=1;             // Show till status every 
                                         // UPDATE_STATS iterations
  static boolean FINAL_ONLY;             // Only show final customer records but
                                         // do not show statistics every iteration.
  static boolean AVERAGE_MAX_ONLY;       // Only show average and maximum values for
                                         // Queue Lengths, Service Times, etc.
  //************************************************************
  // These fields hold various statistics that can be collated *
  // as a simulation procedes.                                 *
  //************************************************************
  static int customers_left=0;                   // The customers remaining in queues
  static int customers_through=0;                // The total number of customers.
  static int longest_queue_length =0;            
  static int longest_queue;
  //****************************************************
  // Fields associated with the Simulation Process     *
  //****************************************************
  static Queue[] CheckOutQueues;       // The Queue at each Checkout;
  static int[] TimeLeftAtTill;         // Time remaining before a till is free.
  static Customer[] CustomerStats;     // Records Customer data.
  //***************************************************
  //   Class Methods                                  *
  //***************************************************
  //
  // Input user settings for this simulation
  //
  public static void SetSimulationParameters() throws IOException
    {
    char YesNo;                        // To indicate statistics required.
    System.out.print("How many checkout tills are there?:");
    MAX_TILLS = new Integer (keyboardInput.readLine()).intValue();
    System.out.println(MAX_TILLS);
    System.out.print("What is the maximum number of customers?");
    MAX_CUSTOMERS = new Integer (keyboardInput.readLine()).intValue();
    System.out.println(MAX_CUSTOMERS);
    System.out.print("What is the maximum number of items a single customer might buy?:");
    MAX_ITEMS = new Integer (keyboardInput.readLine()).intValue();
    System.out.println(MAX_ITEMS);
    System.out.print("For how many rounds are the Check Out Queues Open?:");
    ROUNDS_TO_SIMULATE = new Integer (keyboardInput.readLine()).intValue();
    System.out.println(ROUNDS_TO_SIMULATE);
    System.out.print("How frequently do customers arrive at checkout (0.0 < r < 1.0)?:");
    ARRIVAL_RATE = new Double (keyboardInput.readLine()).doubleValue();
    System.out.println(ARRIVAL_RATE);
    System.out.print("How often should Checkout statistics be shown?:");
    UPDATE_STATS = new Integer (keyboardInput.readLine()).intValue();
    System.out.println(UPDATE_STATS);
    System.out.print("Show status of each queue until completed? (y/n):");
    YesNo = keyboardInput.readLine().charAt(0);
    if ( (YesNo=='y') || (YesNo=='Y') )
      FINAL_ONLY = false;
    else
      FINAL_ONLY = true;
    System.out.println(YesNo); 
    System.out.print("Output Averages and Maxima Only? (y/n):");
    YesNo = keyboardInput.readLine().charAt(0);
    if ( (YesNo=='y') || (YesNo=='Y') )
      AVERAGE_MAX_ONLY = true;
    else
      AVERAGE_MAX_ONLY = false;
    System.out.println(YesNo); 
    System.out.println();
    }
  //*********************************************
  // Initiate Simulation Structures             *
  //*********************************************
  public static void StartUpQueues()
    {
    CheckOutQueues = new Queue[MAX_TILLS];
    TimeLeftAtTill = new int[MAX_TILLS];
    for (int i=0; i<MAX_TILLS; i++)
      CheckOutQueues[i] = new Queue();
    }
  //*********************************************
  // Simulation over when all Qs empty and      *
  // the required number of rounds is over.     *
  //*********************************************
  public static boolean AllCustomersServed()
    {
    boolean ok=true;
    int till_check =0;
    while ((ok) && (till_check < MAX_TILLS))
      {
      ok = ( (ok) && (CheckOutQueues[till_check].QIsEmpty()) );
      till_check++;
      }
    return ok;
    }
  //**********************************************************
  // The following 4 methods output details of the  customer *
  // and checkout queues as the simulation procedes.         *
  //**********************************************************
  static void ShowServedCustomer( int qnum, int cnum )
     {
     System.out.println("Customer at till "+qnum+" is Customer "+cnum+
     " who arrived at  "+ CustomerStats[cnum].get_arrived()+" and is buying "+
     CustomerStats[cnum].get_service_time()+" items. "+
     " Time left to wait is "+ TimeLeftAtTill[qnum]);
     }
  //**********************************************************
  public static void OutputCheckOutStats( int after_round )
    {
    int firstinQ;
    System.out.println("Check Out Tills after "+after_round+" rounds");
    System.out.println("**********************************************");
    for (int i=0; i<MAX_TILLS; i++)
      {
      System.out.print("Check Out Queue "+i+": ");
      System.out.println("      Length: "+CheckOutQueues[i].GetWaiting() );
      if (!CheckOutQueues[i].QIsEmpty())
        {
        firstinQ = 
          Integer.valueOf(CheckOutQueues[i].GetFirstInQ().toString()).intValue();
        ShowServedCustomer(i,firstinQ);
        }
      }
    System.out.println("**********************************************");
    System.out.println("Customers seen: "+customers_through);
    System.out.println("Customers remaining: "+customers_left);
    System.out.println("**********************************************");
    }
  //**********************************************************
  public static void OutputCustomerStats()
    {
    System.out.println("Customer Records after Completion");
    System.out.println("**********************************************");
    for (int i=0; i<customers_through; i++)
      {
      System.out.print("Customer "+i+": Arrived: "+
                           CustomerStats[i].get_arrived());
      System.out.print("   Left: "+
                           CustomerStats[i].get_depart());
      System.out.print("   Bought: "+
                           CustomerStats[i].get_service_time()+
                           " items");
      System.out.println("   at till: "+
                            CustomerStats[i].get_checkout());
      }
    }
  //***********************************************************************
  static void OutputAverageMaxStats( int total_time )
    {
    int longest_wait=0;
    int longest_waiting=0;
    int total_waiting_time=0;
    int temp_wait;
    double average_wait;
    System.out.println("       Average and Maxima Statistics");
    System.out.println("**********************************************");
    System.out.println("Total customers dealt with: "+customers_through);
    System.out.println("Total time to complete serving: "+ total_time);
    System.out.println("Longest queue had length "+
                        longest_queue_length+ " at till "+
                        longest_queue);
    for (int i=0; i<customers_through; i++)
      {
      temp_wait = CustomerStats[i].get_depart()-
                  CustomerStats[i].get_arrived();
      total_waiting_time = total_waiting_time+temp_wait;
      if (temp_wait > longest_wait)
        {
        longest_wait = temp_wait; longest_waiting=i;
        }
      }
    average_wait = total_waiting_time/customers_through;
    System.out.println("Longest wait was "+
                        longest_wait+ " for customer "+
                        longest_waiting + " in check out queue "+
                        CustomerStats[longest_waiting].get_checkout());
    System.out.println("Average waiting time was "+average_wait);
    }
  //***********************************************************************
  // Select a Queue: A simple `greedy' method is used in which any new    *
  // customer is added to the current shortest queue                      *
  // To compare different strategies, all that is needed is to replace    *
  // this method with an alternative.                                     *
  //***********************************************************************
  public static int ChooseQueueToJoin ( Queue[] CheckOuts )
    {
    int shortest_so_far =0;
    for (int i=1; i<MAX_TILLS; i++)
      {
      if (CheckOuts[i].GetWaiting() < CheckOuts[shortest_so_far].GetWaiting() )
        shortest_so_far =i;
      }
    return shortest_so_far;
    }
  //**********************************************************************//
  // The number of items bought by a new customer is randomly chosen to   //
  // to be a value between 1 and MAX_ITEMS.                               //
  //**********************************************************************//
  public static int NumberofItems()
    {
    return 1+(int) Math.rint( (MAX_ITEMS-1)*Math.random() );
    }
  //************************************************************//
  //         Main Method                                        //
  //************************************************************//
  public static void main( String[] args ) throws IOException
    {
    int just_served;                  // To index CustomerStats[]
    int put_on_queue;                 // To index CheckOutQueues[]
    int on_round_number =0;           // Current simulation round
    int firstinQ;                     // Used in updating Queue/Customer
    int nextinQ;                      //     
    Integer CustomerIndex;            // Need Integer to store int index on Queue
    //************************************************************
    SetSimulationParameters();        // Get the user supplied parameters.
    StartUpQueues();                  // Initiate Queues, Tills
    CustomerStats = new Customer[MAX_CUSTOMERS];  // And customer records.
    //************************************************************
    //   Main simulation loop: Continues while customers remain  *
    // or the number of rounds is not completed.                 *
    //************************************************************
    while  ( (!AllCustomersServed()) || 
              (on_round_number < ROUNDS_TO_SIMULATE) )
      {
      on_round_number++;                            // Next simulation round;
      //**********************************************
      // Update the Status of each CheckOut Queue    *
      //**********************************************
      for (int till=0; till<MAX_TILLS; till++)
        {
        //*****************************************************
        // If current till being inspected has a non-empty Q  *
        // then find out the index of the Customer referenced *
        // and decrease the time left until this till is free *
        //*****************************************************
        if (!CheckOutQueues[till].QIsEmpty())
          {
          firstinQ = 
            Integer.valueOf(CheckOutQueues[till].GetFirstInQ().toString()).intValue();
          TimeLeftAtTill[till]--;
          //***************************************************
          // See if this customer has now been served and, if *
          // so update the customer records and queue.        *
          //***************************************************
          if (TimeLeftAtTill[till] < 1)
            {
            // Note the departure time.
            CustomerStats[firstinQ].set_depart(on_round_number);
            CheckOutQueues[till].TakeOffQ();        // Remove customer from queue.
            customers_left--;                       // now have one less customer.
            //*****************************************************
            // If any customer left, serve the next in this Queue *
            //*****************************************************
            if (!CheckOutQueues[till].QIsEmpty())
              {
              nextinQ = 
                Integer.valueOf((CheckOutQueues[till].GetFirstInQ()).toString()).intValue();
              TimeLeftAtTill[till]=CustomerStats[nextinQ].get_service_time();
              }
            }
          }
        }
      //*************************************************************
      // Randomly decide (based on the given ARRIVAL_RATE)          *
      // whether a new customer has appeared. If so, then           *
      // initiate a new Customer record and choose a CheckOutQueue  *
      //*************************************************************
      if ( (Math.random()<ARRIVAL_RATE) &&
           (customers_through<MAX_CUSTOMERS) &&
           (on_round_number < ROUNDS_TO_SIMULATE) )
        {
        CustomerStats[customers_through] = new Customer();
        CustomerStats[customers_through].set_arrived(on_round_number);
        CustomerStats[customers_through].set_service_time(NumberofItems());
        CustomerStats[customers_through].set_id(customers_through);
        put_on_queue = ChooseQueueToJoin(CheckOutQueues);
        //******************************************************
        // If the CheckOutQueue chosen is empty, then the new  *
        // customer will be served at once.                    *
        //******************************************************
        if ( CheckOutQueues[put_on_queue].QIsEmpty() )
          TimeLeftAtTill[put_on_queue] = 
                           CustomerStats[customers_through].get_service_time();
        CustomerStats[customers_through].set_checkout(put_on_queue);
        CustomerIndex = new Integer(customers_through);
        CheckOutQueues[put_on_queue].AddToQ(CustomerIndex);
        //*******************************************************************
        // Test if the queue length is greater than any seen before, and    *
        // update queue length statistics accordingly.                      *
        //*******************************************************************
        if (CheckOutQueues[put_on_queue].GetWaiting() > longest_queue_length)
          {
          longest_queue_length = CheckOutQueues[put_on_queue].GetWaiting();
          longest_queue = put_on_queue;
          }
        customers_left++;               // One more customer in the system.
        customers_through++;            // One more customer seen.
        }
        //*****************************************************************
        //  Output Statistics as required                                 *
        //*****************************************************************
        if ( (on_round_number%UPDATE_STATS==0) &&
             (!FINAL_ONLY) &&
             (!AVERAGE_MAX_ONLY) )
           OutputCheckOutStats(on_round_number);
     }
     //*************************************************************
     // Simulation rounds completed, so output statistcal info.    *
     //*************************************************************
     if (AVERAGE_MAX_ONLY)
       OutputAverageMaxStats(on_round_number);
     else
       {
       OutputCustomerStats();
       OutputAverageMaxStats(on_round_number);
       }
   }
 }     